﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace Doo.DataClass
{
    /// <summary>
    /// 缓存帮助器
    /// </summary>
    public interface ICacheHelper
    {
        /// <summary>
        /// 根据主键值、部分缓存键、未命中获取方法来获取数据
        /// </summary>
        /// <param name="keyvalue"></param>
        /// <param name="cacheKey"></param>
        /// <param name="getObejct"></param>
        /// <returns></returns>
        object Get(string keyvalue, string cacheKey, Func<object> getObejct);
        /// <summary>
        /// 根据主键值、部分缓存键来获取数据，没有命中则返回NULL
        /// </summary>
        /// <param name="keyvalue"></param>
        /// <param name="cacheKey"></param>
        /// <param name="getObejct"></param>
        /// <returns></returns>
        object Get(string keyvalue, string cacheKey);
        /// <summary>
        /// 根据全缓存键来获取数据，没有命中则返回NULL
        /// </summary>
        /// <param name="keyvalue"></param>
        /// <param name="cacheKey"></param>
        /// <param name="getObejct"></param>
        /// <returns></returns>
        object Get(string fullkey);
        /// <summary>
        /// 设置数据到缓存，并设置依赖
        /// </summary>
        /// <param name="keyvalue"></param>
        /// <param name="cacheKey"></param>
        /// <param name="value"></param>
        void Set(string keyvalue, string cacheKey, object value);
        /// <summary>
        /// 设置数据到缓存，并设置依赖
        /// </summary>
        /// <param name="fullkey">完整的缓存键</param>
        /// <param name="value">数据</param>
        /// <param name="keyvalue">数据的主键或标识</param>
        void Set(string fullkey, object value, string keyvalue);
        /// <summary>
        /// 以数据的主键或标识通知更改，一般将从缓存中删除这些数据。
        /// </summary>
        /// <param name="keyvalue"></param>
        void NoticeChange(string keyvalue);
        /// <summary>
        /// 缓存中是否有指定主键或标识和指定部分缓存键的数据
        /// </summary>
        /// <param name="keyvalue"></param>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        bool HasKey(string keyvalue, string cacheKey);
        /// <summary>
        /// 缓存中是否有指定完整缓存键的数据
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        bool HasFullKey(string key);
        /// <summary>
        /// 根据主键或标识及部分缓存键来创建一个完整缓存键
        /// </summary>
        /// <param name="keyvalue"></param>
        /// <param name="cacheKey"></param>
        /// <returns></returns>
        string CreateCacheKey(string keyvalue, string cacheKey);
    }

    #region 自定义缓存基类

    internal class CacheNullValue
    {
        /// <summary>
        /// 空值
        /// </summary>
        public static CacheNullValue NullValue = new CacheNullValue();
    }

    /// <summary>
    /// 缓存帮助器基类
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class CacheHelperBase<T> : ICacheHelper where T : new()
    {
        static T instance;

        /// <summary>
        /// 获取该缓存帮助器的单例
        /// </summary>
        public static T Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new T();
                }
                return instance;
            }
        }

        Dictionary<string, HashSet<string>> CacheKeys = new Dictionary<string, HashSet<string>>();

        /// <summary>
        /// 完整缓存键的前缀
        /// </summary>
        protected abstract string KeyPrefix { get; }

        /// <summary>
        /// 创建缓存键
        /// </summary>
        /// <param name="keyvalue">主键或标识</param>
        /// <param name="cachekey">部分缓存键</param>
        /// <returns>完整缓存键</returns>
        public virtual string CreateCacheKey(string keyvalue, string cachekey)
        {
            return KeyPrefix + cachekey + '_' + keyvalue;
        }

        /// <summary>
        /// 添加缓存，按主键值、唯一键，对象添加。自动产生依赖
        /// </summary>
        /// <param name="keyvalue"></param>
        /// <param name="cacheKey"></param>
        /// <param name="value"></param>
        public virtual void Set(string keyvalue, string cacheKey, object value)
        {
            var cache = HttpRuntime.Cache;
            string fullkey = CreateCacheKey(keyvalue, cacheKey);
            if (value == null) value = CacheNullValue.NullValue;
            cache[fullkey] = value;
            AddCacheDependence(keyvalue, fullkey);
        }

        /// <summary>
        /// 仅设置，无依赖
        /// </summary>
        /// <param name="fullkey"></param>
        /// <param name="value"></param>
        public void Set(string fullkey, object value)
        {
            if (value == null) value = CacheNullValue.NullValue;
            HttpRuntime.Cache[fullkey] = value;
        }

        public void Set(string fullkey, object value, string keyvalue)
        {
            if (value == null) value = CacheNullValue.NullValue;
            HttpRuntime.Cache[fullkey] = value;
            AddCacheDependence(keyvalue, fullkey);
        }

        /// <summary>
        /// 获取一个缓存对象并且当为空时自动产生数据
        /// </summary>
        /// <param name="keyvalue"></param>
        /// <param name="cacheKey"></param>
        /// <param name="getObejct"></param>
        /// <returns></returns>
        public virtual object Get(string keyvalue, string cacheKey, Func<object> getObejct)
        {
            var cache = HttpRuntime.Cache;
            string fullkey = CreateCacheKey(keyvalue, cacheKey);
            //return getObejct();
            var obj = cache[fullkey];
            if (obj == null)
            {
                obj = getObejct();
                if (obj == null) obj = CacheNullValue.NullValue;
                cache[fullkey] = obj;
                AddCacheDependence(keyvalue, fullkey);
            }
            if (obj == CacheNullValue.NullValue) return null;
            return obj;
        }

        public virtual object Get(string keyvalue, string cacheKey)
        {
            var cache = HttpRuntime.Cache;
            string fullkey = CreateCacheKey(keyvalue, cacheKey);
            //return getObejct();
            var obj = cache[fullkey];
            return obj == CacheNullValue.NullValue ? null : obj;
        }

        public object Get(string fullkey)
        {
            var obj = HttpRuntime.Cache[fullkey];
            return obj == CacheNullValue.NullValue ? null : obj;
        }

        public virtual bool HasKey(string keyvalue, string cacheKey)
        {
            var cache = HttpRuntime.Cache;
            string fullkey = CreateCacheKey(keyvalue, cacheKey);
            //return getObejct();
            return cache[fullkey] != null;
        }

        public bool HasFullKey(string key)
        {
            return HttpRuntime.Cache[key] != null;
        }

        /// <summary>
        /// 新增依赖
        /// </summary>
        /// <param name="orgCode"></param>
        /// <param name="cacheKey"></param>
        public virtual void AddCacheDependence(string keyvalue, string fullCacheKey)
        {
            if (!CacheKeys.ContainsKey(keyvalue)) CacheKeys.Add(keyvalue, new HashSet<string>());
            HashSet<string> keys = CacheKeys[keyvalue];
            if (!keys.Contains(fullCacheKey)) keys.Add(fullCacheKey);
        }

        /// <summary>
        /// 通知更改
        /// </summary>
        /// <param name="orgCode"></param>
        public virtual void NoticeChange(string keyvalue)
        {
            if (!CacheKeys.ContainsKey(keyvalue)) return;
            System.Web.Caching.Cache cache = HttpRuntime.Cache;
            foreach (string k in CacheKeys[keyvalue])
            {
                cache.Remove(k);
                Extension.VersionCache.RemoveVersion(k);
            }
        }

        /// <summary>
        /// 清除所有的缓存【非特殊情况勿用】
        /// </summary>
        public void RemoveAll()
        {
            System.Web.Caching.Cache cache = HttpRuntime.Cache;
            foreach (HashSet<string> keys in CacheKeys.Values)
            {
                foreach (string k in keys)
                {
                    cache.Remove(k);
                }
            }
        }
    }

    #endregion

}
